/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.session;

import com.inspur.edp.bef.api.be.BufferClearingType;
import com.inspur.edp.bef.api.be.IBEManager;
import com.inspur.edp.bef.api.lcp.ResponseContext;
import com.inspur.edp.bef.core.action.authorityinfo.BefAuthorityInfo;
import com.inspur.edp.bef.core.action.base.BefCallContext;
import com.inspur.edp.bef.core.be.BEManager;
import com.inspur.edp.bef.core.be.BeBufferChangeManager;
import com.inspur.edp.bef.core.be.BizEntityCacheManager;
import com.inspur.edp.bef.core.be.IBeBufferChangeManager;
import com.inspur.edp.bef.core.lock.LockServiceItem;
import com.inspur.edp.bef.core.scope.BefScopeManager;
import com.inspur.edp.bef.core.session.transaction.IBefTransactionItem;
import com.inspur.edp.bef.spi.auth.DataPermissionCache;
import com.inspur.edp.bef.spi.auth.DataPermissionController;
import com.inspur.edp.cef.api.buffer.ICefBuffer;
import com.inspur.edp.cef.api.changeListener.EntityDataChangeListener;
import com.inspur.edp.cef.api.dataType.entity.ICefRootEntity;
import com.inspur.edp.cef.api.session.ICefSessionItem;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.variable.api.manager.IVariableManager;
import com.inspur.edp.commonmodel.core.session.distributed.DistributedSessionPart;
import com.inspur.edp.commonmodel.core.session.distributed.RootEditToken;
import com.inspur.edp.commonmodel.core.session.distributed.dac.FuncSessionItemIncrement;
import com.inspur.edp.commonmodel.core.variable.VarBufferManager;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
import lombok.Setter;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public final class BEFuncSessionBase implements ICefSessionItem, IBefTransactionItem, Cloneable{

  // region 构造函数

  public BEFuncSessionBase(FuncSession session, String type) {
    this.funcSession = session;
    this.configId = type;
  }

  // endregion

  // region 属性字段
  @Getter
  private final String configId;

  /** 存储BE字典，[Key:DataId, Value:BizEntity] */
  private java.util.HashMap<String, ICefRootEntity> dicBE = new java.util.LinkedHashMap<>();

  protected java.util.HashMap<String, ICefRootEntity> getDicBE() {
    return dicBE;
  }

  private IBEManager beManager;

  public IBEManager getBEManager() {
    return beManager;
  }

  public final void setBEManager(IBEManager value) {
    // com.inspur.edp.bef.core.lcp.createData等方法内部走的是InnerCreateBEManager,
    // BizEntityCacheManager.BEManager为null,
    // 后来再调RetrieveDefault等方法内部走的CreateBEManager会给this.BeManager赋值,
    // 但BizEntityCacheManager.BEManager没有再给赋值了;
    // if (beManager != null)
    //    throw new InvalidOperationException();
    beManager = value;
    if (beManager != null && bizEntityCacheManager != null) {
      bizEntityCacheManager.setBEManager(beManager);
    }
  }

  /**
   * 获取当前BEManager对应的CacheManager
   *
   * @return
   */
  public com.inspur.edp.bef.core.be.BizEntityCacheManager bizEntityCacheManager;

  public BizEntityCacheManager getBizEntityCacheManager() {
    if (bizEntityCacheManager == null) {
      bizEntityCacheManager = new BizEntityCacheManager(getBEManager(), getDicBE());
    }
    return bizEntityCacheManager;
  }

  @Getter
  @Setter
  private Object actionMark;

  @Deprecated
  public BufferClearingType getBufferClearingType() {
    return privateInnerBufferClearingType;
  }

  @Deprecated
  public final void setBufferClearingType(BufferClearingType value) {
    if (getInnerBufferClearingType().getValue() < value.getValue()) {
      setInnerBufferClearingType(value);
    }
  }

  private BufferClearingType privateInnerBufferClearingType = BufferClearingType.None;

  @Deprecated
  public final BufferClearingType getInnerBufferClearingType() {
    return privateInnerBufferClearingType;
  }

  @Deprecated
  public final void setInnerBufferClearingType(BufferClearingType value) {
    privateInnerBufferClearingType = value;
  }

  private ResponseContext privateResponseContext = new ResponseContext();

  public final ResponseContext getResponseContext() {
    return privateResponseContext;
  }

  private BefCallContext callContext;
  public BefCallContext getCallContext(){
    if(callContext == null){
      callContext = new BefCallContext();
    }
    return callContext;
  }

  private LockServiceItem lockContext;
  public LockServiceItem getLockServiceItem(){
    if(lockContext == null){
      lockContext = lazilyEdit(new LockServiceItem(getConfigId()));
    }
    return lockContext;
  }
  // endregion

  // Scope相关，待定
  public static BefScopeManager getBefScopeManager() {
    throw new NotImplementedException();
  }

  // region ICefSession

  private com.inspur.edp.bef.core.be.IBeBufferChangeManager bufferChangeManager;
  public IBeBufferChangeManager getBufferChangeManager(){
    if(bufferChangeManager == null){
      bufferChangeManager = lazilyEdit(new BeBufferChangeManager(funcSession, getConfigId(),
        getBEManager().getAccessorCreator()));
    }
    return bufferChangeManager;
  }

  private <T extends DistributedSessionPart> T lazilyEdit(T item) {
    if(token != null) {
      item.beginEdit(token);
    }
    return item;
  }

  private Optional<VarBufferManager> varBufferManager;

  public VarBufferManager getVarBufferManager() {
    if(varBufferManager == null) {
      IVariableManager varMgr = ((BEManager) getBEManager()).internalCreateVariableManager();
      if (varMgr != null) {
        VarBufferManager buffer = new VarBufferManager(this, varMgr, getConfigId());
        buffer.initBuffer();
        lazilyEdit(buffer);
        varBufferManager = Optional.of(buffer);
      } else {
        varBufferManager = Optional.empty();
      }
    }
    return varBufferManager.orElse(null);
  }

  private Map buffers = new ConcurrentHashMap<>();

  public static final String Category = "BEFuncSessionBase";
  @Override
  public String getCategory() {
    return Category;
  }

  @Override
  public Map<String, Map<Integer, ICefBuffer>> getBuffers() {
    return buffers;
  }

  private Map changes = new ConcurrentHashMap<>();

  @Override
  public java.util.Map<String, Map<Integer, IChangeDetail>> getChanges() {
    return changes;
  }

  private Map<String, ICefRootEntity> rootEntities = new LinkedHashMap<>();

  @Override
  public java.util.Map<String, ICefRootEntity> getRootEntities() {
    return rootEntities;
  }

  private ConcurrentHashMap<Integer, ICefBuffer> variableBuffers= new ConcurrentHashMap<>();

  @Override
  public java.util.Map<Integer, ICefBuffer> getVariableBuffers() {
    return variableBuffers;
  }

  private java.util.HashMap<String, String> repositoryVariables = new java.util.HashMap<>();

  public java.util.HashMap<String, String> getRepositoryVariables() {
    return repositoryVariables;
  }

  @Getter
  @Setter
  private DataPermissionController dataPermissionCtrl;

  @Getter
  @Setter
  private DataPermissionCache dataPermissionCache;

  private Map<String, BefAuthorityInfo> authorityInfos=new HashMap<>();

  public Map<String, BefAuthorityInfo> getAuthorityInfos()
  {
    return authorityInfos;
  }

//  private List<String> opAuths=new ArrayList<>();

  public void addAuthorityInfos(String actionCode,String[] values)
  {
    if(authorityInfos.containsKey(actionCode)==false) {
      BefAuthorityInfo info= new BefAuthorityInfo(actionCode);
      info.addList(values);
      authorityInfos.put(actionCode, info);
    }
    else
    {
      BefAuthorityInfo befAuthorityInfo=authorityInfos.get(actionCode);
      befAuthorityInfo.addList(values);
    }
  }

  public boolean hasValues(String actionCode,String[] values)
  {
    if(getAuthorityInfos().containsKey(actionCode))
      return getAuthorityInfos().get(actionCode).hasValues(values);
    return false;
  }

//  public boolean hasOpAuth(String actionCode)
//  {
//    return  opAuths.contains(actionCode);
//  }
//
//  public void addOpAuth(String actionCode)
//  {opAuths.add(actionCode);
//  }

  private EntityDataChangeListener privateVarListener = EntityDataChangeListener.createInstance();

  @Override
  public final EntityDataChangeListener getVarListener() {
    return privateVarListener;
  }
  // endregion

  //region transaction
  @Override
  public void commit(IBefTransactionItem upper)
  {
    BEFuncSessionBase upperItem = (BEFuncSessionBase)upper;
    getBufferChangeManager().commit(upperItem.getBufferChangeManager());
//    BufferChangeManager.Commit(upperItem.BufferChangeManager);
//    getCallContext().commit(upperItem.getCallContext());
    getLockServiceItem().commit(upperItem.getLockServiceItem());
  }

  @Override
  public void rollBack() {
//    BEFuncSessionBase upperItem = (BEFuncSessionBase)upper;
    getBufferChangeManager().rollBack();
    getLockServiceItem().rollBack();
    //TODO:变量回滚
  }

  @Override
  public IBefTransactionItem begin() {
    BEFuncSessionBase result;
    try {
      result = (BEFuncSessionBase)this.clone();
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
    result.bufferChangeManager = (IBeBufferChangeManager)getBufferChangeManager().begin();
    result.bizEntityCacheManager = getBizEntityCacheManager();
//    result.callContext = (BefCallContext)getCallContext().begin();
    result.lockContext = getLockServiceItem().begin();
    //TODO:变量回滚
    return result;
  }

  private RootEditToken token;

  @Override
  public void beginEdit(RootEditToken token) {
    this.token = token;
    if(bufferChangeManager != null) {
      bufferChangeManager.beginEdit(token);
    }
    if(lockContext != null) {
      lockContext.beginEdit(token);
    }
    if(varBufferManager != null && varBufferManager.isPresent()) {
      varBufferManager.get().beginEdit(token);
    }
    if(!repositoryVariables.isEmpty()) {
      token.putCallContextOnlyAbsent(getConfigId().concat("RepoVar"), new HashMap<>(repositoryVariables));
    }
  }

  @Override
  public void notifySave() {
    if(bufferChangeManager != null) {
      bufferChangeManager.notifySave();
    }
    if(lockContext != null) {
      lockContext.notifySave();
    }
  }

  @Override
  public void endEdit(RootEditToken token) {
    this.token = null;
    if(bufferChangeManager != null) {
      bufferChangeManager.endEdit(token);
    }
    if(lockContext != null) {
      lockContext.endEdit(token);
    }
    if(varBufferManager != null && varBufferManager.isPresent()) {
      varBufferManager.get().endEdit(token);
    }
    if (!FuncSessionUtil.compareMap(getRepositoryVariables(),
        (Map) token.getCallContext().get(getConfigId().concat("RepoVar")))) {
      FuncSessionItemIncrement inc = token.getItemIncrement(getConfigId());
      inc.setCustoms(new HashMap<>());
      inc.getCustoms().put("RepoVar", FuncSessionUtil.convertMap2Binary(getRepositoryVariables()));
    }
  }

//  @Override
//  public FuncSessionItemIncrement buildIncrement(SessionEditToken token) {
//    FuncSessionItemIncrement value = new FuncSessionItemIncrement();
//    if (bufferChangeManager != null) {
//      this.bufferChangeManager.fillIncrement(value);
//    }
//    if (this.varBufferManager != null && this.varBufferManager.isPresent()) {
//      this.varBufferManager.get().fillIncrement(value);
//    }
//    return value;
//  }

  @Getter
  private final FuncSession funcSession;
  //endregion

}
